﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SuperKit
{
    public class CommandExecutor {
        public const char KEY_WORD_SPLIT = '|';
        public const string DEFAULT_OUTPUT_DIR = "(_OUT)";

        public ArgReader Arg { get; }
        protected ConfigReader Cfg => ConfigReader.Instance;
        public IExecutorOwner Parent { get; }
        public string FilePath { get; }
        public int ErrorCount { get; set; }

        protected string Name => Path.GetFileName(FilePath);
        protected string PureName => Path.GetFileNameWithoutExtension(FilePath);
        protected string Dir => Path.GetDirectoryName(FilePath);

        /// <summary>最多处理多少层级</summary>
        public int MaxLayer { get; protected set; }
        /// <summary>是否是测试</summary>
        public bool IsTest { get; protected set; }
        /// <summary>最大的线程数</summary>
        public int MaxThread { get; protected set; }
        /// <summary>目标的目录</summary>
        public string TargetDir { get; protected set; }
        /// <summary>当文件已经存在的时候，是不处理，还是自动改名</summary>
        public bool AutoRename { get; protected set; }
        // 用于当前进度的计数器
        protected int processCount = 0;
        // 用于总项目的计数器
        protected int totalCount = 0;

        private DateTime startTime;

        public CommandExecutor(ArgReader arg, IExecutorOwner form, string fpath) {
            Arg = arg;
            Parent = form;
            FilePath = fpath;
            ErrorCount = 0;
            Items = new List<CommandThread>();
            MaxLayer = arg.GetInteger("-maxLayer", 0);
            IsTest = arg.IsContain("-test");
            MaxThread = arg.GetInteger("-MaxThread", 20);
            TargetDir = arg.GetParameter("-dir");
            AutoRename = arg.IsContain("-autoRename");
            startTime = DateTime.Now;
        }

        public Thread ThreadItem { get; private set; }
        private bool HasNextThread { get => Pointer < Items.Count; }
        private List<CommandThread> Items;
        private int Pointer { get; set; }
        public DirectoryExecutorItem Owner { get; set; }

        public void AddThreadItem(CommandThread item) {
            Items.Add(item);
        }

        public bool LayerEnable(int layer) {
            if (MaxLayer == 0) { // 最大层级为0的时候，表示不对项目进行限制。
                return true;
            }
            return layer < MaxLayer;
        }

        protected void InitProcessCount(int totalCount) {
            this.processCount = 0;
            this.totalCount = totalCount;
        }

        protected void SetTitle(string str) {
            Parent.Title(str);
        }

        protected void SetProcess(string currentItem) {
            SetStatus("[{0}/{1}]{2}", processCount + 1, totalCount, currentItem);
            processCount++;
        }

        protected void SetProcessComplete() {
            SetStatus("[{0}/{0}] - 全部完成", totalCount);
            Flush("[{0}/{0}] - 全部完成", totalCount);
        }

        public void FlushMove(string src, string dest) {
            Flush("Move:{0}", src);
            Flush("To:{0}", dest);
        }

        public virtual void StartThread() {
            Pointer = 0;
            ThreadItem = new Thread(OnThread);
            ThreadItem.Start();
        }

        private void OnThread() {
            while (HasNextThread) {
                Parent.Title($"进行线程项目：{Pointer + 1}/{Items.Count}");
                ExecuteCurrentThread();
            }
            Notice("线程执行完成！");
        }

        protected virtual void ExecuteCurrentThread() {
            var item = Items[Pointer];
            item.Execute();
            Pointer++;
        }

        public void MoveFile(string src, string dest) {
            if (File.Exists(dest)) {
                if (AutoRename) {
                    dest = Kit.GetNonExistName(dest);
                    if (dest.Length == 0) {
                        Flush("{0} 已经存在！无法进行重命名！", dest);
                        return;
                    } else {
                        Flush("重命名：{0}", dest);
                    }
                } else {
                    Flush("{0} 已经存在！", dest);
                    return;
                }                
            }

            if (IsTest) {
                return;
            }

            try {
                FileInfo destInfo = new FileInfo(dest);
                if (destInfo.Directory.Exists == false) {
                    destInfo.Directory.Create();
                }
                File.Move(src, dest);
                SetStatus("Move To  :{0}", dest);
            } catch (IOException e) {
                Flush(e.Message);
            }
        }

        public virtual void Execute() {

        }

        protected virtual void End() {
            Flush($"---- 完成(用时:{Kit.GetPassTime(startTime)}) ----");
            if (Arg.IsClose) {
                if (ErrorCount == 0) {
                    Parent.CloseForm();
                }                
            }
        }

        public void EstablishDirectory(string newPath) {
            var dir = Path.GetDirectoryName(newPath);
            if (!Directory.Exists(dir)) {
                Directory.CreateDirectory(dir);
            }
        }

        public string GetTargetDirFile(FileInfo src) {
            var targetPath = CreateTargetDir();
            var newDir = CalculateNewDir(FilePath, targetPath, src.Directory.FullName);
            if (!Directory.Exists(newDir)) {
                Directory.CreateDirectory(newDir);
            }
            return Path.Combine(newDir, src.Name);
        }

        protected string GetDestDir(string defaultName) {
            string destDir;

            if (!Kit.IsEmpty(TargetDir)) {
                destDir = TargetDir;
            } else {
                destDir = Path.Combine(FilePath, defaultName);
            }

            if (Directory.Exists(destDir) == false) {
                Directory.CreateDirectory(destDir);
            }

            return destDir;
        }

        protected string CreateTargetDir(bool inside = false) {
            var tdir = Kit.IsEmpty(TargetDir) ? "(_TEMP)" : TargetDir;
            var parent = inside ? FilePath : Path.GetDirectoryName(FilePath);

            var targetPath = Path.Combine(parent, tdir);
            if (Directory.Exists(targetPath) == false) {
                Directory.CreateDirectory(targetPath);
            }
            return targetPath;
        }


        /// <summary>
        /// 此函数获得一个输出在Output目录下的文件名。如果通过“-dir”参数指定了，则使用指定的目录，否则使用当前目录下的(_OUT)目录。
        /// </summary>
        /// <param name="file">文件名（仅文件名，不包括路径）</param>
        /// <returns>在Output目录下面的文件</returns>
        protected string GetOutputPath(string name) {
            string containerDir;
            if (Kit.IsEmpty(TargetDir)) {
                containerDir = Path.Combine(FilePath, DEFAULT_OUTPUT_DIR);
            } else {
                containerDir = TargetDir;
            }
            if (Directory.Exists(containerDir) == false) {
                Directory.CreateDirectory(containerDir);
            }

            return Path.Combine(containerDir, name);
        }

        protected static string CalculateNewDir(string parent, string targetPath, string fullName) {
            var dirPath = Path.GetFullPath(parent);

            var leftPath = "";
            try {
                leftPath = fullName.Substring(dirPath.Length + 1); // 这个加1是因为DirPath最后没有那个斜杠，而FullPath有。
            } catch (Exception) {
                // 这个错误是防止在主目录存在的图片。如果是主目录下的图片，fullName下面没有DirPath。
            }
            //var namePath = leftPath.Replace("\\", "_");
            var newPath = Path.Combine(targetPath, leftPath);
            return newPath;
        }

        public static string AdjustName(string name, List<ValueTuple<string, string>> replace) => ReplacerDict.AdjustName(name, replace);

        public void SetStatus(string format, params object[] args) {
            if (format == null) {
                return;
            }
            Parent.Status(string.Format(format, args));
        }
        public void Flush(string format, params object[] args) {
            if (format == null) {
                return;
            }
            Parent.Flush(string.Format(format, args));
        }
        public void Log(string format, params object[] args) {
            if (Owner == null) {
                Flush(format, args);
            } else {
                Parent.Log(Owner.LogIndex, string.Format(format, args));
            }            
        }
        public void SetLog(int index, string format, params object[] args) {
            if (format == null) {
                return;
            }
            Parent.Log(index, string.Format(format, args));
        }

        protected void Notice(string format, params object[] args) {
            Parent.Flush(string.Format("- NOTICE - :" + format, args));
        }
        protected void Error(string format, params object[] args) {
            ErrorCount++;
            Parent.Flush(string.Format("- ERROR - :" + format, args));
        }


        #region 文件处理
        protected static readonly string[] IMG_EXTS = new string[] { ".jpg", ".png", ".bmp", ".gif", ".jpeg" };
        protected static readonly string[] PROCESSED_IMG_EXTS = new string[] { ".jpg", ".png", ".bmp" };
        protected static readonly string[] ZIP_EXTS = new string[] { ".zip", ".rar", ".7z" };
        protected bool IsImage(FileInfo file) => Kit.IsFileType(file, IMG_EXTS);
        protected bool IsZip(FileInfo file) => Kit.IsFileType(file, ZIP_EXTS);

        public int GetAllFiles(DirectoryInfo info, List<FileInfo> list, string[] exts) {
            int count = 0;
            FileInfo[] files = null;
            DirectoryInfo[] dirs = null;
            try {
                files = info.GetFiles();
                dirs = info.GetDirectories();
            } catch (DirectoryNotFoundException e) {
                Flush(e.Message);
            }
            if (files != null) {
                foreach (var i in files) {
                    if (Kit.IsFileType(i, exts)) {
                        list.Add(i);
                        count++;
                    }
                }
            }
            if (dirs != null) {
                foreach (var i in dirs) {
                    if ((i.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden) {
                        count += GetAllFiles(i, list, exts);
                    }
                }
            }
            return count;
        }

        public int GetAllDirectories(DirectoryInfo info, List<DirectoryInfo> list) {
            int count = 0;
            DirectoryInfo[] dirs = null;
            try {
                dirs = info.GetDirectories();
            } catch (DirectoryNotFoundException e) {
                Flush(e.Message);
            }
            if (dirs != null) {
                foreach (var i in dirs) {
                    list.Add(i);
                    count += GetAllDirectories(i, list);
                    count++;
                }
            }
            return count;
        }

        public int GetFileCount(DirectoryInfo info, string[] exts) {
            int count = 0;
            FileInfo[] files = null;
            DirectoryInfo[] dirs = null;
            try {
                files = info.GetFiles();
                dirs = info.GetDirectories();
            } catch (DirectoryNotFoundException e) {
                Flush(e.Message);
            }
            if (files != null) {
                foreach (var i in files) {
                    if (Kit.IsFileType(i, exts)) {
                        count++;
                    }
                }
            }
            if (dirs != null) {
                foreach (var i in dirs) {
                    if ((i.Attributes & FileAttributes.Hidden) != FileAttributes.Hidden) {
                        count += GetFileCount(i, exts);
                    }
                }
            }
            return count;
        }

        #endregion
    }
}
